#include "stdafx.h"
/// Copyright 2012, Alex Averian, Yonan Research
/// Released under BSD Licence


#include "QBase64.h"
using namespace micro::utils;


QBase64::QBase64( void )
{
}


QBase64::~QBase64( void )
{
}

/*
* Convert byte array to base64encoded string.
* @param data		[in]	Byte array of data to be encoded.
* @param len		[in]	Byte length of data.
*
* @return char*	Base64Encoded data(NULL terminated string).
*				You have to delete [] this return value after use.
*/


char * QBase64::encode( const unsigned char * data, unsigned int len )
{

    static const char table[] =
    {
        0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
        0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50,
        0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
        0x59, 0x5A, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66,
        0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E,
        0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76,
        0x77, 0x78, 0x79, 0x7A, 0x30, 0x31, 0x32, 0x33,
        0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x2B, 0x2F
    };

    if ( data == NULL || len == 0 )
    {
        return NULL;
    }

    unsigned int outlen = ( ( len + 2 ) / 3 ) * 4;

    // We create a NULL terminated character string.
    char * out = new char[outlen + 1];
    memset( out, 0, outlen + 1 );
    out[outlen] = '\0';

    const char PADDING = '=';

    int n = 0;

    // Convert 3 bytes to 4 bytes.
    for ( unsigned int i = 0; i < len; i += 3 )
    {
        int remain = len - i;
        int len = ( remain >= 3 ) ? 3 : remain;
        int a, b, c;

        if ( len == 1 )
        {
            a = data[i];
            out[n++] = table[( a >> 2 ) & 0x3F];
            out[n++] = table[( ( a << 4 ) & 0x30 )];
            out[n++] = PADDING;
            out[n++] = PADDING;
        }
        else
            if ( len == 2 )
            {
                a = data[i];
                b = data[i + 1];
                out[n++] = table[( a >> 2 ) & 0x3F];
                out[n++] = table[( ( a << 4 ) & 0x30 ) + ( ( b >> 4 ) & 0xF )];
                out[n++] = table[( ( b << 2 ) & 0x3C )];
                out[n++] = PADDING;
            }
            else
            {
                a = data[i];
                b = data[i + 1];
                c = data[i + 2];
                out[n++] = table[( a >> 2 ) & 0x3F];
                out[n++] = table[( ( a << 4 ) & 0x30 ) + ( ( b >> 4 ) & 0xF )];
                out[n++] = table[( ( b << 2 ) & 0x3C ) + ( ( c >> 6 ) & 0x3 )];
                out[n++] = table[c & 0x3F];
            }
    }

    return out;
}


int QBase64::encode( const unsigned char * bytes, unsigned int size, QString & string )
{
    int rc = 0;
    char * encoded = encode( bytes, size );

    if ( encoded )
    {
        rc = ( int )strlen( encoded );
        string = encoded;		//This converts char string to wchar_t(TCHAR) string.
        delete [] encoded;
    }

    return rc;
}



/*
* Decode base64encoded data.
* @param data		[in] NULL terminated string of Base64Encoded data.
* @param len		[in] Byte length of data.
* @param outBytes	[out] Pointer to pointer for unsigned char* to return decoded data.
* @param outLen	[out] Pointer to unsigned int to return the lenghth of decoded dta.
*/
int QBase64::decode(  const char * data,
             unsigned int len,
             unsigned char *& outBytes,
             unsigned int & outLen )
{

    static const char table[] =
    {
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x3F,
        0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B,
        0x3C, 0x3D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
        0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,
        0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
        0x17, 0x18, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20,
        0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
        0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30,
        0x31, 0x32, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00
    };

    if ( data == NULL || len == 0 )
    {
        return 0;
    }

    if ( outBytes == NULL || outLen == NULL )
    {
        return 0;
    }

    const char PADDING = '=';

    // Compute a length of padding.
    int pad = 0;

    for ( int s = len - 1; s >= 0; s-- )
    {
        if ( data[s] == PADDING )
        {
            pad++;
        }
    }

    // Allocating a memory for decoded area.
    unsigned int length = len * 3 / 4 - pad;

    unsigned char * out = new unsigned char[length];
    memset( out, 0, length );

    unsigned int n = 0;

    //Convert 4 bytes to 3 bytes.
    for ( unsigned int i = 0; i < len; i += 4 )
    {
        int block = ( table[data[i]] << 18 )
                    + ( table[data[i + 1]] << 12 )
                    + ( table[data[i + 2]] << 6 )
                    + ( table[data[i + 3]] );

        for ( unsigned int j = 0; j < 3 && n + j < length; j++ )
        {
            //printf("%d...\n", n+j);
            out[n + j] = ( ( block >> ( 8 * ( 2 - j ) ) ) & 0xff );
        }

        n += 3;
    }

    outBytes = out;
    outLen   = length;

    return length;
}